package com.soft.small.util.encrypt;

/**
 * @author 赵庆旭
 * @version V1.0
 * (C) Copyright 河南紫光物联技术有限公司 Corporation 2018
 * All Rights Reserved.
 * @包名 com.uiot.smarthome.mobile.util.encrypt.util
 * @date 2018-10-26 21:23
 */


import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class Aes {

    /**
     * 加密
     * @param  src 加密字符串
     * @param  key 密钥
     * @return 加密后的字符串
     */
    public static String encrypt(String src, String key) throws Exception {
        // 判断密钥是否为空
        if (key == null) {
            System.out.print("密钥不能为空");
            return null;
        }

        // 密钥补位
        int plus= 16-key.length();
        byte[] data = key.getBytes(StandardCharsets.UTF_8);
        byte[] raw = new byte[16];
        byte[] plusbyte={ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
        for(int i=0;i<16;i++)
        {
            if (data.length > i) {
                raw[i] = data[i];
            } else {
                raw[i] = plusbyte[plus];
            }
        }

        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");	// 算法/模式/补码方式
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(src.getBytes(StandardCharsets.UTF_8));

        //return new Base64().encodeToString(encrypted);//base64
        return binary(encrypted, 16); //十六进制
    }

    /**
     * 解密
     * @param  src 解密字符串
     * @param  key 密钥
     * @return 解密后的字符串
     */
    public static String decrypt(String src, String key) throws Exception {
        try {
            // 判断Key是否正确
            if (key == null) {
                System.out.print("Key为空null");
                return null;
            }

            // 密钥补位
            int plus= 16-key.length();
            byte[] data = key.getBytes(StandardCharsets.UTF_8);
            byte[] raw = new byte[16];
            byte[] plusbyte={ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
            for(int i=0;i<16;i++)
            {
                if (data.length > i) {
                    raw[i] = data[i];
                } else {
                    raw[i] = plusbyte[plus];
                }
            }

            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);

            //byte[] encrypted1 = new Base64().decode(src);//base64
            byte[] encrypted1 = toByteArray(src);//十六进制

            try {
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original, StandardCharsets.UTF_8);
                return originalString;
            } catch (Exception e) {
                System.out.println(e.toString());
                return null;
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            return null;
        }
    }

    /**
     * 将byte[]转为各种进制的字符串
     * @param bytes byte[]
     * @param radix 可以转换进制的范围，从Character.MIN_RADIX到Character.MAX_RADIX，超出范围后变为10进制
     * @return 转换后的字符串
     */
    public static String binary(byte[] bytes, int radix){
        return new BigInteger(1, bytes).toString(radix);	// 这里的1代表正数
    }

    /**
     * 16进制的字符串表示转成字节数组
     *
     * @param hexString 16进制格式的字符串
     * @return 转换后的字节数组
     **/
    public static byte[] toByteArray(String hexString) {
        if (hexString.isEmpty()) {
            throw new IllegalArgumentException("this hexString must not be empty");
        }

        hexString = hexString.toLowerCase();
        final byte[] byteArray = new byte[hexString.length() / 2];
        int k = 0;
        for (int i = 0; i < byteArray.length; i++) {//因为是16进制，最多只会占用4位，转换成字节需要两个16进制的字符，高位在先
            byte high = (byte) (Character.digit(hexString.charAt(k), 16) & 0xff);
            byte low = (byte) (Character.digit(hexString.charAt(k + 1), 16) & 0xff);
            byteArray[i] = (byte) (high << 4 | low);
            k += 2;
        }
        return byteArray;
    }



    private final static String AES = "Aes";

    /**
     * AES加密
     *
     * @param content 需要加密的内容
     * @param password 解密密钥
     * @return
     */
    public static String encryptAes(String content, String password)
    {
        try
        {
            byte[] compBefore = password.getBytes();
            //  byte[] compAfter = Arrays.copyOf(compBefore, 24);
            SecretKeySpec key = new SecretKeySpec(compBefore, AES);
            Cipher cipher = Cipher.getInstance("Aes/ECB/PKCS5Padding");
            byte[] byteContent = content.getBytes(StandardCharsets.UTF_8);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encryptResult = cipher.doFinal(byteContent);
            String result = Base64.encode(parseByte2HexStr(encryptResult).getBytes());
            return result;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }


    public static byte[] encryptAes3(String content, String password)
    {
        try
        {
            byte[] compBefore = password.getBytes();
            //  byte[] compAfter = Arrays.copyOf(compBefore, 24);
            SecretKeySpec key = new SecretKeySpec(compBefore, AES);
            Cipher cipher = Cipher.getInstance("Aes/ECB/PKCS5Padding");
            byte[] byteContent = content.getBytes(StandardCharsets.UTF_8);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encryptResult = cipher.doFinal(byteContent);
            return encryptResult;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * AES解密
     *
     * @param content 待解密内容
     * @param password 解密密钥
     * @return
     */
    public static String decryptAes(String content, String password,boolean zip) {
        try {
            if ("[]".equals(content) || "{}".equals(content)) {
                return content;
            }

            byte[] compBefore = password.getBytes();
//          byte[] compAfter = Arrays.copyOf(compBefore, 24);
            SecretKeySpec key = new SecretKeySpec(compBefore, AES);
            Cipher cipher = Cipher.getInstance("Aes/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decode = Base64.decode(content);
            String s = new String(decode);
            byte[] bytes = parseHexStr2Byte(s);

            if (zip) {
                bytes = SignUtil.decompress(bytes);
            }


            byte[] decryptResult = cipher.doFinal(bytes);
            return new String(decryptResult);
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * AES解密  去掉16进制
     *
     * @param content 待解密内容
     * @param password 解密密钥
     * @return
     */
    public static String decryptAes2(String content, String password,boolean zip) {
        try {
            if ("[]".equals(content) || "{}".equals(content)) {
                return content;
            }

            byte[] compBefore = password.getBytes();
//          byte[] compAfter = Arrays.copyOf(compBefore, 24);
            SecretKeySpec key = new SecretKeySpec(compBefore, AES);
            Cipher cipher = Cipher.getInstance("Aes/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decode = Base64.decode(content);
            if (zip) {
                decode = SignUtil.decompress(decode);
            }
            byte[] decryptResult = cipher.doFinal(decode);
            return new String(decryptResult);
        } catch (Exception e) {
            return null;
        }
    }



    /**
     * AES解密
     *
     * @param content 待解密内容
     * @param password 解密密钥
     * @return
     */
    public static String decryptAes(byte[] content, String password,boolean zip) {
        try {
            byte[] compBefore = password.getBytes();
//          byte[] compAfter = Arrays.copyOf(compBefore, 24);
            SecretKeySpec key = new SecretKeySpec(compBefore, AES);
            Cipher cipher = Cipher.getInstance("Aes/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);

            if (zip) {
                content = SignUtil.decompress(content);
            }
            byte[] decryptResult = cipher.doFinal(content);
            return new String(decryptResult);
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * 将二进制转换成16进制
     *
     * @param buf
     * @return
     */
    private static String parseByte2HexStr(byte[] buf)
    {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++)
        {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1)
            {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString().toLowerCase();
    }

    /**
     * 将16进制转换为二进制
     *
     * @param hexStr
     * @return
     */
    private static byte[] parseHexStr2Byte(String hexStr)
    {
        if (hexStr.length() < 1){
            return null;
        }
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++)
        {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }

    public static final Charset CHARSET = StandardCharsets.UTF_8;

    public static final byte KEY_STRSZIE = 16;

    public static final String ALGORITHM = "Aes";

    public static final String AES_CBC_NOPADDING = "Aes/CBC/NoPadding";



    /**`
     * 为了平台的通用，选择 Aes/CBC/NoPadding 的模式，然后手动 padding
     * 对应PHP 平台为 mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv)
     *
     * Aes/CBC/NoPadding encrypt
     * 16 bytes secretKeyStr
     * 16 bytes intVector
     *
     * @param
     * @param
     * @param input
     * @return
     */
    public static byte[] encryptCbcNoPadding(byte[] secretKeyBytes, byte[] intVectorBytes, byte[] input) {
        try {
            IvParameterSpec iv = new IvParameterSpec(intVectorBytes);
            SecretKey secretKey = new SecretKeySpec(secretKeyBytes, ALGORITHM);
            int inputLength = input.length;
            int srcLength;

            Cipher cipher = Cipher.getInstance(AES_CBC_NOPADDING);
            int blockSize = cipher.getBlockSize();
            byte[] srcBytes;
            if (0 != inputLength % blockSize) {
                srcLength = inputLength + (blockSize - inputLength % blockSize);
                srcBytes = new byte[srcLength];
                System.arraycopy(input, 0, srcBytes, 0, inputLength);
            } else {
                srcBytes = input;
            }

            cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
            byte[] encryptBytes = cipher.doFinal(srcBytes);
            return encryptBytes;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * Aes/CBC/NoPadding decrypt
     * 16 bytes secretKeyStr
     * 16 bytes intVector
     *
     * @param
     * @param
     * @param input
     * @return
     */
    public static byte[] decryptCbcNoPadding(byte[] secretKeyBytes, byte[] intVectorBytes, byte[] input) {
        try {
            IvParameterSpec iv = new IvParameterSpec(intVectorBytes);
            SecretKey secretKey = new SecretKeySpec(secretKeyBytes, ALGORITHM);

            Cipher cipher = Cipher.getInstance(AES_CBC_NOPADDING);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
            byte[] encryptBytes = cipher.doFinal(input);
            return encryptBytes;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 用 Aes 算法加密 inputStr。
     * 使用 secretStr 作为 key，secretStr 的前 16 个字节作为 iv。
     *
     * @param secretStr
     * @param inputStr
     * @return
     */
    public static byte[] encode(String secretStr, String inputStr) {
        if (KEY_STRSZIE > secretStr.length()) {
            return null;
        }
        byte[] secretKeyBytes = secretStr.getBytes(CHARSET);
        secretKeyBytes = Arrays.copyOfRange(secretKeyBytes, 0, 16);
        byte[] ivBytes = Arrays.copyOfRange(secretKeyBytes, 0, 16);
        byte[] inputBytes = inputStr.getBytes(CHARSET);

        byte[] outputBytes = encryptCbcNoPadding(secretKeyBytes, ivBytes, inputBytes);
        return outputBytes;
    }


    /**
     * 用 Aes 算法加密 inputStr。
     * 使用 secretStr 作为 key，secretStr 的前 16 个字节作为 iv。
     * 并对加密后的字节数组调用 sun.misc.BASE64Encoder.encode 方法，
     * 转换成 base64 字符串返回。
     *
     * @param secretStr
     * @param inputStr
     * @return
     * @throws
     */
    public static String strEncodBase64(String secretStr, String inputStr){
        String base64Str = Base64.encode(encode(secretStr, inputStr));
        return base64Str;
    }

    /**
     * 用 Aes 算法加密 inputStr。
     * 使用 secretStr 作为 key，secretStr 的前 16 个字节作为 iv。
     *
     * @param secretStr
     * @param
     * @return
     * @throws
     */
    public static byte[] decode(String secretStr, byte[] inputBytes){
        if (KEY_STRSZIE > secretStr.length()) {
            return null;
        }
        byte[] secretKeyBytes = secretStr.getBytes(CHARSET);
        secretKeyBytes = Arrays.copyOfRange(secretKeyBytes, 0, 16);
        byte[] ivBytes = Arrays.copyOfRange(secretKeyBytes, 0, 16);

        byte[] outputBytes = decryptCbcNoPadding(secretKeyBytes, ivBytes, inputBytes);
        return outputBytes;
    }

    /**
     * 用 Aes 算法解密 inputStr。
     *
     *
     * @param secretStr
     * @param inputStr
     */
    public static String base64StrDecode(String secretStr, String inputStr){
        byte[] inputBytes = null;
        inputBytes = Base64.decode(inputStr);
        String outputStr = new String(decode(secretStr, inputBytes), CHARSET);
        return outputStr;
    }





    public static void main(String[] args) throws Exception {
        // 密钥
        String key = "1234567812345678";
        // 需要加密的字符串
        String src = "abcd1234567890河南紫光物联";

        System.out.println(src);

        // 加密
        String enString = encrypt(src, key);
        System.out.println("加密后的字串是：" + enString);

        // 解密
        String deString = decrypt(enString, key);
        System.out.println("解密后的字串是：" + deString);
        System.out.println("Base64加密后的字符串" + Base64.encode(src.getBytes()));
    }








}

